retryWithExponentialBackoff

suspend fun <T> retryWithExponentialBackoff(policy: RetryPolicy = RetryPolicy(), shouldRetry: (Throwable) -> Boolean = { true }, block: suspend (attempt: Int) -> T): T

Retry block with exponential backoff, honoring server Retry-After headers, and surfacing the full attempt history as a RetryError on exhaustion — the contract upstream's retryWithExponentialBackoff exposes.

Terminal outcomes (mirroring upstream's four throw paths):

  • retries disabled (maxRetries == 0) → the bare original error, unwrapped;

  • a non-retryable error on the first attempt → the bare error, unwrapped;

  • a non-retryable error on a later attempt → RetryError (ErrorNotRetryable) carrying every collected error;

  • retries exhausted → RetryError (MaxRetriesExceeded) carrying every error.

CancellationException is always rethrown first; the delay between attempts is coroutine-cancellable, so a cancelled caller stops waiting immediately (the structured-concurrency equivalent of upstream's abortSignal).